// SuperBlock.cpp
//
// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can alternatively use *this file* under the terms of the the MIT
// license included in this package.

#include <new>
#include <string.h>
#include <unistd.h>

#include "Debug.h"
#include "SuperBlock.h"

using std::nothrow;

/*!
	\class DirEntry
	\brief Represents the on-disk structure for superblock of the FS.

	There exist two versions of the structure and this class can deal with both
	of them. This class can also handle a very old layout that puts the super
	block in a different location. The Init() methods tries to find and read
	the superblock from disk.
*/

// read_super_block
template<typename super_block_t>
static
status_t
read_super_block(int device, off_t offset, const char *magic,
				 super_block_t **_superBlock)
{
	super_block_t *superBlock = NULL;
	status_t error = B_OK;
	// allocate memory for the superblock
	if (error == B_OK) {
		superBlock = new(nothrow) super_block_t;
		if (!superBlock)
			error = B_NO_MEMORY;
	}
	// read the superblock
	if (error == B_OK) {
		size_t size = sizeof(super_block_t);
		if (read_pos(device, offset, superBlock, size) != (int32)size)
			error = B_IO_ERROR;
	}
	// check magic
	if (error == B_OK) {
		size_t len = strlen(magic);
		if (strncmp(superBlock->s_magic, magic, len))
			error = B_ERROR;
	}
	// set result / cleanup on failure
	if (error == B_OK)
		*_superBlock = superBlock;
	else if (superBlock)
		delete superBlock;
	return error;
}

// constructor
SuperBlock::SuperBlock()
	: fFormatVersion(REISERFS_3_6),
	  fCurrentData(NULL)
{
}

// destructor
SuperBlock::~SuperBlock()
{
	if (GetFormatVersion() == REISERFS_3_6) {
		if (fCurrentData)
			delete fCurrentData;
	} else {
		if (fOldData)
			delete fOldData;
	}
}

// Init
status_t
SuperBlock::Init(int device, off_t offset)
{
	status_t error = B_OK;
	// try old version and old layout
	if (read_super_block(device, REISERFS_OLD_DISK_OFFSET_IN_BYTES + offset,
						 REISERFS_SUPER_MAGIC_STRING, &fOldData) == B_OK) {
PRINT(("SuperBlock: ReiserFS 3.5 (old layout)\n"));
		fFormatVersion = REISERFS_3_5;
	// try old version and new layout
	} else if (read_super_block(device, REISERFS_DISK_OFFSET_IN_BYTES + offset,
			REISERFS_SUPER_MAGIC_STRING, &fOldData) == B_OK) {
PRINT(("SuperBlock: ReiserFS 3.5\n"));
		fFormatVersion = REISERFS_3_5;
	// try new version and new layout
	} else if (read_super_block(device, REISERFS_DISK_OFFSET_IN_BYTES + offset,
			REISER2FS_SUPER_MAGIC_STRING, &fCurrentData) == B_OK) {
PRINT(("SuperBlock: ReiserFS 3.6\n"));
		fFormatVersion = REISERFS_3_6;
	// failure
	} else
		error = B_ERROR;
	// TODO: checks...
	return error;
}


// GetLabel
status_t
SuperBlock::GetLabel(char* buffer, size_t bufferSize) const
{
	if (GetFormatVersion() == REISERFS_3_6 && fCurrentData->s_label[0]) {
		size_t len = MIN(sizeof(fCurrentData->s_label), bufferSize - 1);
		memcpy(buffer, fCurrentData->s_label, len);
		buffer[len] = '\0';
		return B_OK;
	}
	return B_ENTRY_NOT_FOUND;
}
